#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <stdint.h>

using namespace std;

const string suffix(".IFF");

map<string,string> read_dict(ifstream &in) {
  string key, val;
  map<string,string> dict;

  while(in) {
    in >> key;
    if(key == "$") break;
    //in >> ch;
    getline(in, val);
    dict[key] = val.substr(1);
  }
  return dict;
}

ostream &write(uint32_t val, ostream &out, int bits = 32) {
  for(int i = bits - 8; i >= 0; i -= 8) {
    out << static_cast<unsigned char>((val >> i) & 0xFF);
  }
  return out;
}

void write_data(ifstream &in, const map<string,string> &dict, ofstream &out) {
  string data, key;
  int i;
  //uint8_t u8;
  //int8_t s8;
  uint32_t u32;

  out << "FORM";
  out.seekp(4, ios::cur);
  data = dict.at("FORM");
  out << data;
  while(in) {
    if(!(in >> key)) break;
    map<string,string>::const_iterator fit(dict.find(key));
    if(fit != dict.end()) {
      long pos = (out << (key + "   ").substr(0, 4)).tellp();
      out.seekp(4, ios::cur);
      getline(in, data);
      const string format(fit->second);
      istringstream sdata(data);
      sdata >> hex;
      for(unsigned i = 0; i < format.size(); ++i) {
	cout << format[i] << endl;
	if(format[i] == 'x') {
	  out << '\0';
	} else if(format[i] == 'c') {
	  char ch;
	  sdata >> ch;
	  out << ch;
	} else {
	  sdata >> u32;
	  switch(format[i]) {
	  case 'b':
	  case 'B':
	    out << static_cast<unsigned char>(u32);
	    break;
	  case 'w':
	  case 'W':
	    out << static_cast<unsigned char>(u32 >> 8);
	    out << static_cast<unsigned char>(u32);
	    break;
	  case 'l':
	  case 'L':
	    write(u32, out, 32);
	    break;
	  }
	}
      }
      long posend = out.tellp();
      out.seekp(pos);
      write(posend - pos - 4, out, 32);
      out.seekp(posend);
      if(posend & 1) out << '\0';
    } else {
      cout << "Warning! Unknown chunk '" << key << "'.\n";
    }
  }
  long pos = out.tellp();
  cout << pos << endl;
  pos -= 8;
  out.seekp(4);
  for(i = 24; i >= 0; i -= 8) {
    out << static_cast<unsigned char>((pos >> i) & 0xFF);
  }
}

int main(int c, char *argv[]) {
  if(c != 2) {
    cerr << "Usage: formpack FILE\n";
    return 5;
  }
  ifstream in(argv[1]);
  if(!in) {
    cerr << "Can not open '" << argv[1] << "'!\n";
    return 5;
  }
  map<string,string> known_chunks(read_dict(in));
  for(auto it = known_chunks.begin(); it != known_chunks.end(); ++it) {
    cout << '"' << it->first << "\"\t\"" << it->second << "\"\n";
  }
  ofstream out(argv[1] + suffix, ios::binary);
  write_data(in, known_chunks, out);
  return 0;
}
